package com.awesomeproject;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.Xml;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.UiThreadUtil;

import org.xmlpull.v1.XmlPullParser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class PostModule extends ReactContextBaseJavaModule {
    private final String TAG = "RNUpdateModule";
    private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f' };

    //服务器地址；
    List<String> descriptions;//版本描述信息；
    String APKurl;
    String md5;
    int localVersion,newVersion;

    volatile boolean cancelDownload = false;
    volatile long apkSize = 0;
    volatile long currentSize = 0;

    checkThread mCheckThread = null;
    DownloadThread mDownloadThread = null;
    AlertDialog updateDialog;
    ProgressBar bar;
    Timer timer;

    private final int UPDATE_ERROR_CREATE_FILE = 0x22;
    private final int UPDATE_ERROR_NETWORK = 0x33;
    private final int UPDATE_ERROR_MD5CHECK = 0x44;
    private final int UPDATE_START_INSTALL = 0x55;
    private final int UPDATE_DOWNLOAD_PROGRESS = 0x77;

    Handler handler = new Handler(Looper.getMainLooper()){//获取主线程Looper新建Handler
        @Override
        public void handleMessage(Message msg){
            switch(msg.what){
                case UPDATE_ERROR_CREATE_FILE:
                    if (updateDialog != null) {
                        updateDialog.dismiss();
                    }
                    //Toast.makeText(getCurrentActivity(),"未插入sd卡",Toast.LENGTH_SHORT).show();
                    if(timer!=null) {
                        timer.cancel();
                        timer = null;
                    }
                    break;
                case UPDATE_ERROR_NETWORK:
                    if (updateDialog != null) {
                        updateDialog.dismiss();
                    }
                    //Toast.makeText(getCurrentActivity(),"请检查网络设置！",Toast.LENGTH_SHORT).show();
                    if(timer!=null) {
                        timer.cancel();
                        timer = null;
                    }
                    break;
                case UPDATE_ERROR_MD5CHECK:
                    if (updateDialog != null){
                        updateDialog.dismiss();
                    }

                    if(timer!=null) {
                        timer.cancel();
                        timer = null;
                    }
                    break;


                case UPDATE_START_INSTALL://开启安装。
                    if (updateDialog != null) {
                        updateDialog.dismiss();
                    }

                    if(timer!=null) {
                        timer.cancel();
                        timer = null;
                    }

                    if (!cancelDownload) {
                        Uri uri = Uri.fromFile((File) msg.obj); //获取文件的Uri
                        Intent installIntent = new Intent(Intent.ACTION_VIEW);
                        installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        installIntent.setDataAndType(uri,
                                "application/vnd.android.package-archive");//设置intent的数据类型
                        getCurrentActivity().startActivity(installIntent);
                    }
                    break;

                case UPDATE_DOWNLOAD_PROGRESS://更新进度条。
                    if (bar != null) {
                        bar.setMax((int) apkSize);
                        bar.setProgress((int) currentSize);
                    }

                    if ((apkSize == currentSize) || cancelDownload) {
                        if (timer != null) {
                            timer.cancel();
                            timer = null;
                        }
                    }
                    break;
            }
        }
    };

    public PostModule(final ReactApplicationContext reactContext){
        super(reactContext);
    }

    @Override
    public String getName(){
        return"PostAndroid";
    }

    @ReactMethod
    public void startUpdate(final String url, final String md5) {
        final PostModule that = this;
        UiThreadUtil.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                cancelDownload = false;
                synchronized (this) {
                    if (mDownloadThread != null) {
                        alertWithProgressBar("下载中...");//弹出下载进度对话框。
                        return;
                    }

                    mDownloadThread = new DownloadThread(that, url, md5);
                    mDownloadThread.start();
                    alertWithProgressBar("下载中...");//弹出下载进度对话框。
                    refreshProgress();//使用Timer向handler发送消息，定时刷新进度条。
                }

            }
        });
    }

    @ReactMethod
    public void checkVersion(final String updateUrl, final Callback successCallback, final Callback errorCallback) {
        final PostModule that = this;
        UiThreadUtil.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    if (mCheckThread != null)
                        return;

                    if (mDownloadThread != null) {
                        alertWithProgressBar("下载中...");//正在下载，弹出下载进度栏。
                        return;
                    }
                    mCheckThread = new checkThread(that, updateUrl, successCallback, errorCallback);
                    mCheckThread.start();
                }
            }
        });
    }

    private static PackageInfo getPackageInfo(Context context) {
        PackageInfo pi = null;

        try {
            PackageManager pm = context.getPackageManager();
            pi = pm.getPackageInfo(context.getPackageName(),
                    PackageManager.GET_CONFIGURATIONS);

            return pi;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pi;
    }

    //根据APKurl创建文件路径。
    public static String toHexString(byte[] b) {
        StringBuilder sb = new StringBuilder(b.length * 2);
        for (int i = 0; i < b.length; i++) {
            sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]);
            sb.append(HEX_DIGITS[b[i] & 0x0f]);
        }
        return sb.toString();
    }//手机SD卡创建保存apk的文件。

    public View initProgressBar() {
        bar = new ProgressBar(getCurrentActivity(), null, android.R.attr.progressBarStyleHorizontal);
        LinearLayout layout = new LinearLayout(getCurrentActivity());
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.setMargins(30, 0, 30, 0);
        layout.setLayoutParams(layoutParams);
        bar.setLayoutParams(layoutParams);
        layout.addView(bar);
        return layout;
    }//新建ProgressBar。

    public void alertWithProgressBar(String message) {//弹出带进度条的对话框。
        AlertDialog.Builder builder = new AlertDialog.Builder(getCurrentActivity());
        builder.setTitle("更新");
        builder.setMessage(message);
        builder.setPositiveButton("后台下载", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });
        builder.setNegativeButton("取消下载", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                cancelDownload = true;
            }
        });
        builder.setView(initProgressBar());
        updateDialog = builder.show();
    }

    public void refreshProgress() {
        if (timer != null)
            return;

        timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                handler.sendEmptyMessage(UPDATE_DOWNLOAD_PROGRESS);
            }
        };
        timer.schedule(task, 100, 1000);
    }//Timer刷新进度条。

    class checkThread extends Thread {
        PostModule mHost;
        String updateUrl;
        Callback successCallback;
        Callback errorCallback;

        public checkThread(PostModule host, String updateUrl, Callback successCallback, Callback errorCallback) {
            mHost = host;
            this.updateUrl = updateUrl;
            this.successCallback = successCallback;
            this.errorCallback = errorCallback;
        }

        private void realRun() {
            try {
                //获取服务器XML数据。
                URL url = new URL(updateUrl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(30000);

                if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    InputStream inputStream = conn.getInputStream();

                    XmlPullParser parser = Xml.newPullParser();

                    parser.setInput(inputStream, "UTF-8");
                    int eventType = parser.getEventType();
                    descriptions = new ArrayList<>();
                    while (eventType != XmlPullParser.END_DOCUMENT) {
                        switch (eventType) {
                            case XmlPullParser.START_DOCUMENT:
                                Log.i(TAG, "检查更新：" + "开始解析xml文件");
                                break;
                            case XmlPullParser.START_TAG:
                                if ("ver".equals(parser.getName())) {
                                    newVersion = (int) Double.parseDouble(parser.nextText());
                                    //ver标签转Double再转int
                                    localVersion = getPackageInfo(getReactApplicationContext())
                                            .versionCode;

                                    if (localVersion >= newVersion) {
                                        errorCallback.invoke("已是最新版了哦~");
                                        return;
                                    }

                                    break;
                                }
                                if ("desc".equals(parser.getName())) {
                                    String desc = parser.nextText();
                                    descriptions.add(desc);
                                    break;
                                }
                                if ("url".equals(parser.getName())) {
                                    APKurl = parser.nextText();
                                    break;
                                }
                                if ("md5".equals(parser.getName())) {
                                    md5 = parser.nextText();
                                    break;
                                }
                                break;

                            case XmlPullParser.END_TAG:
                                break;
                            default:
                                break;

                        }
                        eventType = parser.next();
                    }

                    //显示对话框。
                    String describution = descriptions.get(0);
                    for (int i = 1; i < descriptions.size(); i++) {
                        describution += "\n";
                        describution += descriptions.get(i);
                    }
                    successCallback.invoke(describution, APKurl, md5);
                }
            } catch (Exception e) {
                e.printStackTrace();
                handler.sendEmptyMessage(UPDATE_ERROR_CREATE_FILE);
            }
        }

        @Override
        public void run() {
            realRun();
            synchronized (mHost) {
                mHost.mCheckThread = null;
            }
        }
    }

    class DownloadThread extends Thread {
        private PostModule mHost;
        private String ApkUrlString;
        private String md5OnServer;

        public DownloadThread(PostModule host, String ApkUrl, String md5) {
            mHost = host;
            this.ApkUrlString = ApkUrl;
            this.md5OnServer = md5;

        }

        private void realRun() {
            try {
                Log.i(TAG, "新线程:" + "开启一条新线程，， ");
                URL downloadUrl = new URL(ApkUrlString);

                HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
                conn.setConnectTimeout(30000);
                conn.setRequestMethod("GET");

                File file = getFile(ApkUrlString);
                if (file.exists()) {
                    currentSize = file.length();
                    if (currentSize > 0) {
                        Log.i(TAG, "断点续传" + "开始位置：" + currentSize);
                        conn.setRequestProperty("Range", "bytes=" + currentSize + "-");
                    }
                }

                FileOutputStream fileOutputStream = null;
                InputStream is = null;

                int code = conn.getResponseCode();
                if (code == 416) {//已下载完成的开启安装。
                    md5CheckInstall(file);//检查MD5值。
                }
                Log.i(TAG, "code:" + "" + code);
                if (code == 200 || code == 206) {
                    if (code == 200)
                        apkSize = conn.getContentLength();
                    //第一次下载获取到的是服务器apk的大小，但会为文件预分配apk大小的空间。
                    if (code == 206)
                        apkSize = conn.getContentLength() + currentSize;
                    //断点续传时，获取到剩余的文件大小。读取到的文件大小为已下载apk的大小。
                    Log.i(TAG, "APK大小" + apkSize);
                    Log.i(TAG, "文件大小" + file.length());
                    is = conn.getInputStream();
                    if (is != null) {
                        try {
                            fileOutputStream = new FileOutputStream(file, true);
                            byte[] buffer = new byte[10240];
                            int length = 0;
                            Log.i(TAG, "下载:" + "开始下载");

                            while ((length = is.read(buffer)) != -1 && !cancelDownload) {//开始下载。
                                fileOutputStream.write(buffer, 0, length);
                                currentSize += length;
                            }

                            fileOutputStream.flush();
                            fileOutputStream.close();

                            Log.i(TAG, "下载：" + "下载中止");
                            Log.i(TAG, "开始MD5校验");
                            if (currentSize == apkSize) {
                                md5CheckInstall(file);
                            }
                        } catch (Exception e) {
                            Log.d(TAG, "错误：" + "打开存储卡失败，请插入存储卡");
                            handler.sendEmptyMessage(UPDATE_ERROR_CREATE_FILE);
                            e.printStackTrace();
                        }

                    }

                    if (is != null) {
                        is.close();
                    }
                }

                Log.i(TAG, "文件大小" + "" + file.length());
            } catch (Exception e) {
                e.printStackTrace();
                handler.sendEmptyMessage(UPDATE_ERROR_NETWORK);
                Log.d(TAG, "错误：" + "请检查网络设置！");
            }
        }

        public void run() {
            realRun();

            synchronized (mHost) {
                mHost.mDownloadThread = null;
            }
        }

        private String getFilePath(String url) {
            return url.substring(url.lastIndexOf("/"), url.length());
        }

        //获取到的MD5值转换为String类型。
        private File getFile(String url) {
            File files = new File(Environment.getExternalStorageDirectory()
                    .getAbsoluteFile(), getFilePath(url));
            return files;
        }
        private void  md5CheckInstall(File file){
           try{
               MessageDigest msgMd5 = MessageDigest.getInstance("MD5");
               InputStream md5In = new FileInputStream(file);
               int numRead = 0;
               byte[] md5FileInput = new byte[4096];
               while ((numRead = md5In.read(md5FileInput)) > 0) {
                   msgMd5.update(md5FileInput, 0, numRead);

                   //传递一次循环下载的长度，更新ProgressBar。
               }
               String localMD5 = toHexString(msgMd5.digest());
               md5In.close();
               if(localMD5.equals(md5OnServer))
               {
                   Message msg = Message.obtain(handler,UPDATE_START_INSTALL,file);
                   handler.sendMessage(msg);
               }
               else{
                   file.delete();
                   handler.sendEmptyMessage(UPDATE_ERROR_MD5CHECK);
               }
           }
           catch (Exception e){
               e.printStackTrace();
           }
        }
    }

}


